var STATE_CLEAR = 0;
var STATE_SPLIT = 1;
var STATE_MERGE = 2;

function Move(board, color, id, validator, cloneCallback) {
  this.color = color;
  this.id = id;
  this.originalBoard = board;
  this.validator = validator;
  this.cloneCallback = cloneCallback;
  this.reset();
}

Move.prototype.reset = function() {
  this.board = this.originalBoard.clone(this.cloneCallback);
  this.state = STATE_CLEAR;
  this.splitDir = UNKNOWN_DIR;
  this.splitLine = ZERO;
  this.splitStart = null;
  this.splitEnd = null;
  this.mergeLeftTop = null;
  this.mergeLeftBottom = null;
  this.mergeRightTop = null;
  this.mergeRightBottom = null;
  this.message = "";
  this.swappablePieces = new Array();
  this.swappedPieces = new Array();
  this.numSwappedPieces = 0;
  this.winningColor = NULL_COLOR;
  this.board.evaluate();
  this.capturedAtSwap = false;
}

Move.prototype.split = function(piece, dir, fake) {
  if (this.validator.isEnabled()) {
	  if (piece.color != this.color) {
	    this.message = "You can't split your opponent's piece";
	    return false;
	  }
	  if (this.state == STATE_MERGE) {
	    this.message = "Can not split. Already merging";
	    return false;
	  }
  }
  if (!piece.canSplit(dir)) {
    this.message = "The piece can not be split in " + DIR_TEXT[dir] + " direction";
    return false;
  }
  if (!this.validator.isEnabled()) {
      if (!fake) {
        this.board.split(piece,dir);
      }
      this.message = "";
      return true;
  }
  if (this.state == STATE_CLEAR) {
    if (!fake) {
	    this.state = STATE_SPLIT;
	    this.splitDir = dir;
	    this.splitLine = piece.getMiddle(dir);
	    this.splitStart = piece.start[dir];
	    this.splitEnd = piece.end[dir];
        this._addToSwappablePieces(this.board.split(piece, dir));
    }
    this.message = "";
    return true;
  } else if (this.state == STATE_SPLIT && this.splitDir == dir && piece.getMiddle(dir).equals(this.splitLine) &&
       !this._isSwapped(piece)) {
    if (piece.start[dir].equals(this.splitEnd)) {
      if (!fake) {
        this.splitEnd = piece.end[dir];
      }
    } else if (piece.end[dir].equals(this.splitStart)) {
      if (!fake) {
        this.splitStart = piece.start[dir];
      }
    } else {
      var pFirst = this.board.equiStart([this.splitLine, this.splitStart]);
      if (dir == HORIZONTAL) {
        pFirst = this.board.equiStart([this.splitStart, this.splitLine]);
      }
      var pLast = piece;
      if (pLast.end[dir].smaller(pFirst.start[dir])) {
        var temp = pFirst;
        pFirst = pLast;
        pLast = temp;
      }
      return this.longSplit(pFirst, pLast, dir, fake);
    }
    if (!fake) {
      this._addToSwappablePieces(this.board.split(piece, dir));
    }
    this.message = "";
    return true;
  } else {
    this.message = "The piece is not a part of long split";
    return false;
  }
}

Move.prototype.splitOrSwap = function(piece, dir, side) {
  if (this.numSwappedPieces > 0 && this._swappable(piece)) {
    this.swap(piece, side);
  } else {
    this.split(piece,dir);
  }
}

Move.prototype.longSplit = function(pFirst, pLast, dir, fake) {
  if (this.validator.isEnabled()) {
	  if (pFirst.color != this.color || pLast.color != this.color) {
	    this.message = "You can't split your opponent's piece";
	    return false;
	  }
	  if (this.state == STATE_MERGE) {
	    this.message = "Can not do long split. Already merging";
	    return false;
	  }
  }
  if (!pFirst.canSplit(dir)) {
    this.message = "The piece can not be split in " + DIR_TEXT[dir] + " direction";
    return false;
  }
  if (!this.validator.isEnabled()) {
    if (!fake) {
      this.board.longSplit(pFirst, pLast, dir);
    }
    this.message = "";
    return true;
  }
  if (this.state == STATE_CLEAR) {
    if (fake) {
      return this.board.canLongSplit(pFirst, pLast, dir);
    }
    this.state = STATE_SPLIT;
    this.splitDir = dir;
    this.splitLine = pFirst.getMiddle(dir);
    this.splitStart = pFirst.start[dir];
    this.splitEnd = pLast.end[dir];
    assert(!pFirst.end[dir].bigger(pLast.start[dir]));
    var res = this.board.longSplit(pFirst, pLast, dir);
    if (res.length == 0) {
      this.message = "Failed to do long split"
      return false;
    }
    this._addToSwappablePieces(res);
    this.message = "";
    return true;
  } else if (this.state == STATE_SPLIT && this.splitDir == dir
      && pFirst.getMiddle(dir).equals(this.splitLine)
      && pLast.getMiddle(dir).equals(this.splitLine) ) {
    if (!fake) {
	    if (pFirst.start[dir].equals(this.splitEnd)) {
	      this.splitEnd = pLast.end[dir];
	    } else if (pLast.end[dir].equals(this.splitStart)) {
	      this.splitStart = pFirst.start[dir];
	    }
		var res = this.board.longSplit(pFirst, pLast, dir);
	    if (res.length == 0) {
	      this.message = "Failed to do long split"
	      return false;
	    }
	    this._addToSwappablePieces(res);
	    this.message = "";
	    return true;
	} else {
	  return this.board.canLongSplit(pFirst, pLast, dir);
	}
  } else {
    this.message = "The piece is not a part of long split";
    return false;
  }
}


Move.prototype.merge = function(pLeftTop, pRightTop, pLeftBottom, pRightBottom) {
  if (this.validator.isEnabled()) {
	  if (pLeftTop.color != this.color) {
	    this.message = "You can't merge your opponent's pieces";
	    return;
	  }
	  if (this.state != STATE_CLEAR) {
	    this.message = "Can not merge. Already splitting";
	    return;
	  }
  }
  if (!this.board.canMerge(pLeftTop, pRightTop, pLeftBottom, pRightBottom)) {
    this.message = "The pieces can not be merged. Check size proportions, alignment and colors";
    return;
  }
  this.mergeLeftTop = pLeftTop;
  this.mergeRightTop = pRightTop;
  this.mergeLeftBottom = pLeftBottom;
  this.mergeRightBottom = pRightBottom;
  if (!this.validator.isEnabled()) {
    this.board.merge(pLeftTop, pRightTop, pLeftBottom, pRightBottom);
    return;
  }
  this.state = STATE_MERGE;
  this._addToSwappablePieces(this.board.merge(pLeftTop, pRightTop, pLeftBottom, pRightBottom));
  this.message = "";
}

Move.prototype.swap = function(piece, side, fake) {
  if (!this.validator.isEnabled()) {
    if (!fake) {
      piece.swap();
    }
    this.message = "";
    return true;
  }
  if (this.capturedAtSwap) {
    this.message = "Can not swap. Your piece was captured. Cancel move and try again";
    return false;
  }
  if (this.state == STATE_CLEAR) {
    this.message = "You need to split or merge before you swap";
    return false;
  }
  for (var i = this.numSwappedPieces-1; i > 0; i--) {
    if (this.swappedPieces[i] == piece && !this._swappable(piece)) {
      if (!fake) {
       // revert everything starting this one
        this.board.resetCapturedPieces(i);
        for (var j = i+1; j < this.numSwappedPieces; j++) {
          var foundBefore = false;
          for (var k = 0; k < i; k++) {
            if (this.swappedPieces[k] == this.swappedPieces[j]) {
              foundBefore = true;
              break;
            }
          }
          if (!foundBefore) {
            this.swappedPieces[j].swapped = false;
          }
        }
        if (this.numSwappedPieces -1 != i) {
          this.swappedPieces[this.numSwappedPieces-1].swap();
          this.swappedPieces[i].swap()
        }
        this.numSwappedPieces = i+1;
      }
      this.message = "";
      return true;
    }
  }
  if (Board.prototype._findPiece(this.swappablePieces, piece) && !this._swappable(piece)) {
    if (!fake) {
      for (var j = 1; j < this.numSwappedPieces; j++) {
        this.swappedPieces[j].swapped = false;
      }
      this.message = "";
      this.board.resetCapturedPieces(0);

      if (piece.swapped) {
        if (this.numSwappedPieces > 1) {
          this.swappedPieces[this.numSwappedPieces-1].swap();
        }
        this.swappedPieces = new Array();
        this.numSwappedPieces = 0;
        piece.swapped = false;
        piece.color = this.color;
      } else {
        if (this.numSwappedPieces > 0) {
	      this.swappedPieces[0].color = this.color;
	      this.swappedPieces[0].swapped = false;
  	      if (this.numSwappedPieces > 1 && this.swappedPieces[this.numSwappedPieces-1] != this.swappedPieces[0]) {
	        this.swappedPieces[this.numSwappedPieces-1].swap();
          }
          this.swappedPieces = new Array();
        }
        piece.swapped = true;
        piece.swap();
        this.swappedPieces[0] = piece;
        this.numSwappedPieces = 1;
      }
    }
    return true;
  } /* else if (this.numSwappedPieces == 0) {
    this.message = "You need to start swap with the pieces you split or merged";
    return false;
  } */
  if (this.numSwappedPieces == 0 && piece.color != this.color) {
    var from = this._findPieceToSwapFrom(piece, side);
    if (from == null) {
       this.message = "Can not swap to this piece";
       return false;
    } else {
       var canSwap = this.swap(from, UNKNOWN_SIDE, fake);
       if (!canSwap) {
         this.message = "Can not swap to this piece";
         return false;
       }
       if (canSwap && fake) {
         return true;
       }
    }
  }
  if (this._swappable(piece)) {
    if (this.numSwappedPieces > 1) {
      if (!fake) {
        this.swappedPieces[this.numSwappedPieces-1].swap();
      }
    }
    if (!fake) {
      if (this.board.isPieceCaptured(piece, OPPOSITE_COLOR[this.color])) {
        //this.swappedPieces[this.numSwappedPieces-1].swap(); // swap back
        this.message = "Your piece is captured during swap";
        this.capturedAtSwap = true;
        piece.captured = true;
        piece.swap();
        return true;
      }
      piece.swapped = true;
      piece.swap();
      this.numSwappedPieces +=1;
      this.swappedPieces[this.numSwappedPieces -1] = piece;
      this.board.evaluate();
      this.board.captureIsolatedPieces(false, this.numSwappedPieces);
    }
    this.message = "";
    return true;
  }
  return false;
}

Move.prototype.complete = function() {
  if (this.winningColor != NULL_COLOR) {
    return true; // resigned
  }
  if (this.validator.isEnabled() && this.state == STATE_CLEAR) {
    this.message = "The move is empty";
    return false;
  }
  if (this.validator.isEnabled() && this.numSwappedPieces == 1) {
    this.message = "The swap is incomplete";
    return false;
  }

  this.board.evaluate();
  this.winningColor = this.board.getWinningColor();
  if (this.winningColor != NULL_COLOR) {
    this.message = COLOR_TEXT[this.winningColor] + " has won!";
  }
  if (this.validator.isEnabled()) {
    this.board.captureIsolatedPieces(true);
  }
  return true;
}


Move.prototype._swappable = function(piece) {
  if (piece.color == this.color) {
    this.message = "Only " + COLOR_TEXT[OPPOSITE_COLOR[this.color]] + " can be swapped";
    return false;
  }
  if (piece.isolated) {
    this.message = "This piece is already captured by you, can't swap.";
    return false;
  }
  var p = this.swappedPieces[this.numSwappedPieces -1];
  if (Board.prototype.aligned(piece, this.swappedPieces[this.numSwappedPieces-1])) {
    return true;
  }
  this.message = "This piece is not properly aligned with the previously swapped piece";
  return false;
}

Move.prototype._isSwapped = function(piece) {
  for (var i = this.numSwappedPieces-1; i > 0; i--) {
    if (this.swappedPieces[i] == piece) {
      return true;
    }
  }
  return false;
}

Move.prototype._addToSwappablePieces = function(pieces) {
  for (var i = 0; i < pieces.length; i++) {
    this.swappablePieces.push(pieces[i]);
    pieces[i].swappable = true;
  }
}

Move.prototype._findPieceToSwapFrom = function(piece, side) {
  assert (piece.color != this.color);
  var sides = [0, 1, 2, 3];
  if (side != UNKNOWN_SIDE) {
    // put preferred side first;
    sides[0] = side;
    sides[side] = 0;
  }
  for (var i = 0; i < 4; i++) {
    if (piece.connections[sides[i]].length > 0) {
      var from = piece.connections[sides[i]][0];
      if (from.color == this.color && Board.prototype.aligned(from, piece) &&
          Board.prototype._findPiece(this.swappablePieces, from)) {
         return from;
      }
    }
  }
  return null;
}


toTest.push(Move);


Move.prototype.test = function() {
}